// win
package win

import (
	"fmt"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/axgle/mahonia"
)

//字符集转换
func UseNewEncoder(src string, oldEncoder string, newEncoder string) string {
	srcDecoder := mahonia.NewDecoder(oldEncoder)
	desDecoder := mahonia.NewDecoder(newEncoder)
	resStr := srcDecoder.ConvertString(src)
	_, resBytes, _ := desDecoder.Translate([]byte(resStr), true)
	return string(resBytes)
}

//执行cmd命令行命令
func ExecCommand(cmd []string) (string, error) {
	args := append([]string{"/c"}, cmd...)
	exe := exec.Command("cmd", args...)
	buf, err := exe.CombinedOutput()
	out := UseNewEncoder(string(buf), "gbk", "utf8")
	out = strings.TrimSpace(out)
	if err != nil {
		return "", fmt.Errorf("执行[%s]失败,ExitCode:%d,Err:%s", strings.Join(exe.Args, " "), exe.ProcessState.ExitCode(), out)
	}
	return out, nil
}

//reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp" /v PortNumber /t REG_DWORD /d 0xFFF2 /f
//reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber /t REG_DWORD /d 0xFFF2 /f
func RegAddDword(reg_key, prop_name string, prop_val int) error {
	cmd := []string{"reg", "add", reg_key, "/v", prop_name, "/t", "REG_DWORD", "/d", fmt.Sprintf("0x%04x", prop_val), "/f"}
	_, err := ExecCommand(cmd)
	return err
}

func RegQueryDword(reg_key, prop_name string) (int, error) {
	cmd := []string{"reg", "query", reg_key, "/v", prop_name}
	out, err := ExecCommand(cmd)
	if err != nil {
		return 0, err
	}

	reg := regexp.MustCompile(`0x([0-9a-fA-F]+)`)
	p := reg.FindStringSubmatch(out)[1]
	pi, err := strconv.ParseUint(p, 16, 16)
	// p = fmt.Sprint(pi)
	return int(pi), err
}

//执行 wmic <alias> get <fields> /value
func WmicGetValueSingleRow(alias, fields string) (map[string]string, error) {
	out, err := ExecCommand([]string{"wmic", alias, "get", fields, "/value"})
	if err != nil {
		return nil, err
	}
	l := ParseWmicOutput2Map(out)
	return l, nil
}

//执行 wmic <alias> get <fields> /value
func WmicGetValue(alias, fields string) ([]map[string]string, error) {
	out, err := ExecCommand([]string{"wmic", alias, "get", fields, "/value"})
	if err != nil {
		return nil, err
	}
	l := ParseWmicOutput2List(out)
	return l, nil
}

//查询远程桌面端口号
// reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp" /v PortNumber
func GetRdp() (int, error) {
	key := `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp`
	return RegQueryDword(key, "PortNumber")
}

//修改远程桌面端口
func SetRdp(port int) error {
	key := `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp`
	pn := "PortNumber"
	if err := RegAddDword(key, pn, port); err != nil {
		return err
	}

	key = `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp`
	if err := RegAddDword(key, pn, port); err != nil {
		return err
	}
	return nil
}

//设定系统时钟
func SetDatetime(now time.Time) error {
	nd := now.Format("2006-01-02")
	nt := now.Format("15:04:05")
	cmd := []string{"date", nd, "&&", "time", nt}
	_, err := ExecCommand(cmd)
	return err
}

//修改用户密码
func ChangePasswd(user, pass string) error {
	_, err := ExecCommand([]string{"net", "user", user, pass})

	return err
}

//将wmic * /value的k=v格式输出拆解为map
func ParseWmicOutput2Map(str string) map[string]string {
	m := map[string]string{}
	for _, line := range strings.Split(str, "\n") {
		line = strings.TrimSpace(line)
		if i := strings.Index(line, "="); i > 0 {
			k := strings.TrimSpace(line[:i])
			v := strings.TrimSpace(line[i+1:])
			// fmt.Println(line, "拆解", k, v)
			m[k] = v
		}
	}
	return m
}

//将wmic * /value的k=v格式输出拆解为list每个元素为一个map
func ParseWmicOutput2List(str string) []map[string]string {
	rows := make([]map[string]string, 0)
	row := map[string]string{}
	for _, line := range strings.Split(strings.TrimSpace(str), "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			if len(row) > 0 {
				rows = append(rows, row)
				row = map[string]string{}
			}
			continue
		}
		if i := strings.Index(line, "="); i > 0 {
			k := strings.TrimSpace(line[:i])
			v := strings.TrimSpace(line[i+1:])
			row[k] = v
		}
	}
	if len(row) > 0 {
		rows = append(rows, row)
		row = map[string]string{}
	}
	return rows
}

//机器物理内存总量/B
func GetMemTotalSize() (int64, error) {
	if r, e := WmicGetValueSingleRow("COMPUTERSYSTEM", "TotalPhysicalMemory"); e == nil {
		return strconv.ParseInt(r["TotalPhysicalMemory"], 10, 64)
	} else {
		return 0, e
	}
}

//各个盘符的空闲字节数,如 C: = 10323320
func GetVolumnFreeSpace() (map[string]int64, error) {
	r, e := WmicGetValue("LOGICALDISK", "Caption,FreeSpace")
	if e != nil {
		return nil, e
	}
	m := make(map[string]int64, len(r))
	for _, it := range r {
		c := it["Caption"]
		s := it["FreeSpace"]
		iv, e := strconv.ParseInt(s, 10, 64)
		if e != nil {
			return m, fmt.Errorf("%s无效数据[%s]:%s", c, s, e.Error())
		}
		m[c] = iv
	}

	return m, nil
}

//cpu负载百分比
func GetCpuLoadPercentage() (int, error) {
	if r, e := WmicGetValueSingleRow("cpu", "LoadPercentage "); e == nil {
		iv, e := strconv.ParseInt(r["LoadPercentage"], 10, 16)
		return int(iv), e
	} else {
		return 0, e
	}
}

//查询Cpu元数据 DeviceID,ProcessorId,MaxClockSpeed,Name
func GetCpuMetadata() (map[string]string, error) {
	return WmicGetValueSingleRow("cpu", "DeviceID,ProcessorId,MaxClockSpeed,Name")
}

//内存使用率 FreePhysicalMemory,FreeSpaceInPagingFiles,FreeVirtualMemory
//如果配置了虚拟内存 PagefileCurrentUsage,PagefilePeakUsage
func GetMemFreeSize() (map[string]int64, error) {
	r, e := WmicGetValueSingleRow("OS", "FreePhysicalMemory,FreeSpaceInPagingFiles,FreeVirtualMemory")
	if e != nil {
		return nil, e
	}
	m := make(map[string]int64, 3)
	for k, v := range r {
		iv, e := strconv.ParseInt(v, 10, 64)
		if e != nil {
			return m, fmt.Errorf("%s无效数据[%s]:%s", k, v, e.Error())
		}
		m[k] = iv
	}

	if m2, e := WmicGetValueSingleRow("Pagefile", `AllocatedBaseSize,CurrentUsage,PeakUsage`); e == nil {
		if v, has := m2["CurrentUsage"]; has {
			iv, _ := strconv.Atoi(v)
			m["PagefileCurrentUsage"] = int64(iv)
		}
		if v, has := m2["PeakUsage"]; has {
			iv, _ := strconv.Atoi(v)
			m["PagefilePeakUsage"] = int64(iv)
		}
		if v, has := m2["AllocatedBaseSize"]; has {
			iv, _ := strconv.Atoi(v)
			m["PagefileBaseSize"] = int64(iv)
		}
	}

	return m, nil
}

//查询磁盘分区信息 Caption,VolumeName,Description,FileSystem,Size,FreeSpace
func GetVolumnMetadata() ([]map[string]string, error) {
	return WmicGetValue("LOGICALDISK", "Caption,Description,FileSystem,FreeSpace,Size,VolumeName")
}

//查询系统启动时间、已运行时长
func GetLastBootUpTime() (a time.Time, b time.Duration, e error) {
	var m map[string]string
	m, e = WmicGetValueSingleRow("OS", "LastBootUpTime,LocalDateTime")
	if e != nil {
		return
	}
	a = ParseWmicOutput_Time(m, "LastBootUpTime")
	b = ParseWmicOutput_Time(m, "LocalDateTime").Sub(a)
	return
}

func ParseWmicOutput_Time(m map[string]string, key string) time.Time {
	if sv, has := m[key]; has {
		t, _ := time.ParseInLocation("20060102150405", sv[:14], time.Local)
		return t
	}
	return time.Time{}
}
func ParseWmicOutput_Int64(m map[string]string, key string) int64 {
	if sv, has := m[key]; has {
		if iv, e := strconv.ParseInt(sv, 10, 64); e == nil {
			return iv
		}
	}
	return 0
}

//主机信息： Bios,Model,TotalPhysicalMemory,Name,NumberOfProcessors,NumberOfLogicalProcessors
func GetComputerMetadata() (map[string]string, error) {
	alias := "COMPUTERSYSTEM"
	fields := `Model,TotalPhysicalMemory,Name,NumberOfProcessors,NumberOfLogicalProcessors`
	m, e := WmicGetValueSingleRow(alias, fields)
	if e != nil {
		return m, e
	}
	if t := ParseWmicOutput_Int64(m, "TotalPhysicalMemory"); t > 0 {
		m["TotalPhysicalMemory"] = fmt.Sprintf("%dG", t/1e9)
	}
	if mm, e := WmicGetValueSingleRow("bios", `Name,Manufacturer,Version`); e == nil {
		p := []string{}
		for _, v := range mm {
			v = strings.TrimSpace(v)
			if v != "" {
				p = append(p, v)
			}
		}
		m["Bios"] = strings.Join(p, "-")
	}
	return m, nil
}

//物理硬盘信息 Caption,SerialNumber,Size,Partitions
func GetDiskMetadata() ([]map[string]string, error) {
	alias := "DISKDRIVE"
	fields := `Caption,SerialNumber,Size,Partitions`
	return WmicGetValue(alias, fields)
}

//操作系统信息： Caption,Version,WindowsDirectory,InstallDate,Pagefile
func GetOsMetadata() (map[string]string, error) {
	alias := "os"
	fields := `Caption,Version,WindowsDirectory,InstallDate`
	m, e := WmicGetValueSingleRow(alias, fields)
	if e != nil {
		return m, e
	}
	if t := ParseWmicOutput_Time(m, "InstallDate"); !t.IsZero() {
		m["InstallDate"] = t.Format(time.RFC3339)
	}

	if m2, e := WmicGetValueSingleRow("Pagefile", `Caption,AllocatedBaseSize,CurrentUsage,PeakUsage`); e == nil {
		if v, has := m2["AllocatedBaseSize"]; has {
			m["Pagefile"] = fmt.Sprintf("%s:%sM", m2["Caption"], v)
		}
	}
	return m, nil
}

//查询网卡 Name,Mac,Ipv4,Flag,Dns
func GetEthMetadata() []map[string]string {
	intf, err := net.Interfaces()
	if err != nil {
		return nil
	}

	wk_lst := make([]map[string]string, 0, len(intf))
	for _, v := range intf {
		if v.Flags&net.FlagLoopback != 0 {
			continue
		}
		wk := map[string]string{"Name": v.Name, "Mac": v.HardwareAddr.String(), "Flag": v.Flags.String()}
		//拨号连接的网络 v.Flags & net.FlagPointToPoint !=0
		iplist := make([]string, 0, 2)
		if ips, err := v.Addrs(); err == nil {
			for _, ip := range ips {
				if m, e := regexp.MatchString(`^\d+\.\d+\.\d+\.\d+`, ip.String()); e == nil && m {
					iplist = append(iplist, ip.String())
				}
			}
		}
		wk["Ipv4"] = strings.Join(iplist, ",")
		wk_lst = append(wk_lst, wk)
	}

	if dnsmap, err := GetDnsServers(); err == nil {
		for _, wk := range wk_lst {
			wkmc := wk["Name"]
			if dns, has := dnsmap[wkmc]; has {
				wk["Dns"] = dns
				delete(dnsmap, wkmc)
			}
		}
		for name, dns := range dnsmap {
			wk_lst = append(wk_lst, map[string]string{"Name": name, "DNS": dns})
		}
	}

	if ipsets, err := GetIpv4Settings(); err == nil {
		for _, ipset := range ipsets {
			var find bool
			for _, wk := range wk_lst {
				if wk["Name"] == ipset["Name"] {
					wk["DHCP"] = ipset["DHCP"]
					if v, has := ipset["Mask"]; has {
						wk["Mask"] = v
					}
					if v, has := ipset["Gateway"]; has {
						wk["Gateway"] = v
					}
					find = true
					break
				}
			}
			if !find {
				wk_lst = append(wk_lst, ipset)
			}
		}
	}
	return wk_lst
}

//所有网卡的DNS 网卡名称=dns列表
func GetDnsServers() (map[string]string, error) {
	out, err := ExecCommand(strings.Split("netsh interface ip show dns", " "))
	if err != nil {
		return nil, err
	}
	lines := strings.Split(out, "\n")
	reg1 := regexp.MustCompile(`^接口\s+"(.+)"`)
	reg2 := regexp.MustCompile(`DNS.*\s(\d+\.\d+\.\d+\.\d+)$`)
	reg3 := regexp.MustCompile(`^(\d+\.\d+\.\d+\.\d+)$`)

	wk := make(map[string]string, len(lines))
	var name string
	var dnss []string
	for i := 0; i < len(lines); i++ {
		line := strings.TrimSpace(lines[i])
		if m := reg1.FindStringSubmatch(line); len(m) > 0 {
			if len(dnss) > 0 {
				wk[name] = strings.Join(dnss, ",")
			}
			name = m[1]
			dnss = make([]string, 0, 2)
		} else if m := reg2.FindStringSubmatch(line); len(m) > 0 {
			dnss = append(dnss, m[1])
		} else if m := reg3.FindStringSubmatch(line); len(m) > 0 {
			dnss = append(dnss, m[1])
		}
	}
	if len(dnss) > 0 {
		wk[name] = strings.Join(dnss, ",")
	}
	return wk, nil
}

//所有网卡的IP设置 Name,DHCP,Mask,Gateway
func GetIpv4Settings() ([]map[string]string, error) {
	out, err := ExecCommand(strings.Split("netsh interface ip show address", " "))
	if err != nil {
		return nil, err
	}

	lines := strings.Split(out, "\n")

	reg1 := regexp.MustCompile(`^接口\s+"(.+)"`)
	reg2 := regexp.MustCompile(`^DHCP.*\s(\S+)$`)
	reg3 := regexp.MustCompile(`^IP.*\s(\d+\.\d+\.\d+\.\d+)\s*$`)
	reg4 := regexp.MustCompile(`子网.*(255.\d+\.\d+\.\d+).*$`)
	reg5 := regexp.MustCompile(`网关.*\s(\d+\.\d+\.\d+\.\d+).*$`)

	lst := make([]map[string]string, 0)
	var wk map[string]string
	for i := 0; i < len(lines); i++ {
		line := strings.TrimSpace(lines[i])
		if m := reg1.FindStringSubmatch(line); len(m) > 0 {
			// fmt.Println("网卡", m[1])
			if len(wk) > 0 {
				lst = append(lst, wk)
			}
			wk = make(map[string]string, 5)
			wk["Name"] = m[1]
		} else if m := reg2.FindStringSubmatch(line); len(m) > 0 {
			if "是" == m[1] {
				wk["DHCP"] = "Y"
			} else if "否" == m[1] {
				wk["DHCP"] = "N"
			} else {
				wk["DHCP"] = m[1]
			}
		} else if m := reg3.FindStringSubmatch(line); len(m) > 0 {
			// fmt.Println("IP", m[1])
			wk["IP"] = m[1]
		} else if m := reg4.FindStringSubmatch(line); len(m) > 0 {
			// fmt.Println("子网掩码", m[1])
			wk["Mask"] = m[1]
		} else if m := reg5.FindStringSubmatch(line); len(m) > 0 {
			// fmt.Println("默认网关", m[1])
			wk["Gateway"] = m[1]
		}
	}
	if len(wk) > 0 {
		lst = append(lst, wk)
	}
	return lst, nil
}
